package rexsee.core.browser;

import java.io.StringReader;
import java.util.ArrayList;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;

import rexsee.core.application.RexseeApplication;
import rexsee.core.browser.clazz.JavascriptInterface;
import rexsee.core.lang.RexseeLanguage;
import rexsee.core.transportation.DownloadFile;
import rexsee.core.transportation.RexseeAjax;
import rexsee.core.transportation.RexseeDownload;
import rexsee.core.utilities.Utilities;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;

public class RexseeClient implements JavascriptInterface {

	public static final String INTERFACE_NAME = "Client";
	@Override
	public String getInterfaceName() {
		return mBrowser.application.resources.prefix + INTERFACE_NAME;
	}
	@Override
	public JavascriptInterface getInheritInterface(Browser childBrowser) {
		return this;
	}
	@Override
	public JavascriptInterface getNewInterface(Browser childBrowser) {
		return new RexseeClient(childBrowser);
	}

	public final class ClientDescription {
		public String xml = null;
		public int version = 0;
		public String error = null;
		public ArrayList<DownloadFile> files = null;
		@Override
		public String toString() {
			String f = "";
			if (files != null) {
				for (int i = 0; i < files.size(); i++) {
					if (i > 0) f += ",";
					f += "{";
					f += "\"url\":\"" + files.get(i).url + "\"";
					f += ",\"path\":\"" + files.get(i).path + "\"";
					f += "}";
				}
			}
			f = "[" + f + "]";
			String rtn = "{";
			rtn += "\"version\":" + String.valueOf(version);
			rtn += ",\"error\":\"" + error + "\"";
			rtn += ",\"files\":" + f;
			rtn += "}";
			return rtn;
		}
	}

	public static final String PREFERENCE_NAME = "client";

	private final Context mContext;
	private final Browser mBrowser;
	private ClientDescription syncClientDescription = null;

	public RexseeClient(Browser browser) {
		super();
		mBrowser = browser;
		mContext = browser.getContext();
	}

	private String getVersionKey(String xml) {
		xml = mBrowser.urlListeners.getAbsoluteUrl(xml);
		if (xml.contains("?")) xml = xml.substring(0, xml.indexOf("?"));
		return "client_" + Utilities.md5(xml, true);
	}
	private String removeLeadingDotAndSlash(String str) {
		if (str == null) return null;
		while (str.startsWith(".") || str.startsWith("/")) {
			if (str.length() <= 1) return "";
			else str = str.substring(1);
		}
		return str;
	}
	private ClientDescription parseXML_(final String xml, final String code, boolean showProgress) {

		if (showProgress) mBrowser.progressDialog.show(RexseeLanguage.PROGRESS_ONGOING);
		Thread parseThread = new Thread() {
			@Override
			public void run() {
				ClientDescription client = new ClientDescription();
				XmlPullParserFactory factory;
				try {
					factory = XmlPullParserFactory.newInstance();
				} catch (XmlPullParserException e) {
					client.error = "Exception on getting XmlPullParserFactory: " + e.getLocalizedMessage();
					syncClientDescription = client;
					return;
				}
				factory.setNamespaceAware(true);
				XmlPullParser xpp;
				try {
					xpp = factory.newPullParser();
				} catch (XmlPullParserException e) {
					client.error = "Exception on getting XmlPullParser: " + e.getLocalizedMessage();
					syncClientDescription = client;
					return;
				}
				try {
					xpp.setInput(new StringReader(code));
				} catch (XmlPullParserException e) {
					client.error = "Exception on setInput of XmlPullParser: " + e.getLocalizedMessage();
					syncClientDescription = client;
					return;
				}
				int eventType;
				try {
					eventType = xpp.getEventType();
				} catch (XmlPullParserException e) {
					client.error = "Exception on getEventType of XmlPullParser: " + e.getLocalizedMessage();
					syncClientDescription = client;
					return;
				}

				while (eventType != XmlPullParser.END_DOCUMENT) {
					if (eventType == XmlPullParser.START_TAG) {
						String tagName = xpp.getName().toLowerCase();
						int count = xpp.getAttributeCount();
						if (tagName.equals("client")) {
							for (int i = 0; i < count; i++) {
								if (xpp.getAttributeName(i).toLowerCase().equals("version")) {
									try {
										client.version = Integer.parseInt(xpp.getAttributeValue(i));
									} catch (Exception e) {
										client.error = "Invalid version code.";
										syncClientDescription = client;
										return;
									}
								}
							}
						} else if (tagName.equals("file")) {
							DownloadFile file = new DownloadFile();
							for (int i = 0; i < count; i++) {
								String name = xpp.getAttributeName(i).toLowerCase();
								String value = xpp.getAttributeValue(i);
								if (name.equals("url")) {
									file.url = value;
								} else if (name.equals("path")) {
									file.path = value;
								}
							}
							if (file.url != null && file.path != null) {
								if (client.files == null) client.files = new ArrayList<DownloadFile>();
								client.files.add(file);
							}
						}
					}
					try {
						eventType = xpp.next();
					} catch (Exception e) {
						client.error = "Exception on parsing tag:" + e.getLocalizedMessage();
						syncClientDescription = client;
						return;
					}
				}
				if (client.files == null || client.files.size() == 0) {
					client.error = "Exception: Didn't find any file to download.";
				} else {
					for (int i = 0; i < client.files.size(); i++) {
						DownloadFile file = client.files.get(i);
						file.url = RexseeUrl.getAbsoluteUrl(mBrowser.urlListeners.getAbsoluteUrl(xml), removeLeadingDotAndSlash(file.url));
						file.path = RexseeUrl.getAbsoluteUrl(getRoot(xml), removeLeadingDotAndSlash(file.path));
					}
				}
				client.xml = xml;
				syncClientDescription = client;
				return;
			}
		};
		syncClientDescription = null;
		parseThread.start();
		while (syncClientDescription == null) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
			}
		}
		parseThread = null;
		if (showProgress) mBrowser.progressDialog.hide();
		return syncClientDescription;

	}
	private ClientDescription getClient_(String xml, boolean showProgress, boolean cancelable) {
		ClientDescription client;
		RexseeAjax ajax = (RexseeAjax) mBrowser.interfaceList.get(mBrowser.application.resources.prefix + RexseeAjax.INTERFACE_NAME);
		if (ajax == null) {
			client = new ClientDescription();
			client.error = "Ajax object is not available.";
			return client;
		}
		String ajaxRtn = ajax.syncSubmit(xml, "", mBrowser.application.getDefaultEncoding(), showProgress, cancelable);
		if (ajaxRtn.startsWith(RexseeApplication.EXCEPTION_PREFIX)) {
			client = new ClientDescription();
			client.error = ajaxRtn;
		} else {
			client = parseXML_(xml, ajaxRtn, showProgress);
		}
		return client;
	}

	//JavaScript Interface

	public String getRoot(String xml) {
		return "file://" + mContext.getDir(getVersionKey(xml), Context.MODE_PRIVATE).getAbsolutePath() + "/";
	}
	public String getHomepage(String xml) {
		return getRoot(xml) + "index.html";
	}
	public int getVersion(String xml) {
		SharedPreferences preference = mContext.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
		return (preference == null) ? -1 : preference.getInt(getVersionKey(xml), -1);
	}
	public void load(String xml) {
		mBrowser.function.load(getHomepage(xml));
	}
	public void launch(String xml) {
		mBrowser.application.setDeveloperHome(getHomepage(xml));
		mBrowser.application.confirmRestart();
	}

	public int getLatestVersion(String xml) {
		ClientDescription client = getClient_(xml, false, false);
		return (client.error == null) ? client.version : -1;
	}
	public String getXML(String xml) {
		RexseeAjax ajax = (RexseeAjax) mBrowser.interfaceList.get(mBrowser.application.resources.prefix + RexseeAjax.INTERFACE_NAME);
		if (ajax == null) { return RexseeApplication.EXCEPTION_PREFIX + "Ajax object is not available."; }
		return ajax.syncSubmit(xml, "", mBrowser.application.getDefaultEncoding(), true, true);
	}
	public String getJson(String xml) {
		return getClient_(xml, true, true).toString();
	}
	public String build(String xml) {
		ClientDescription client = getClient_(xml, true, true);
		if (client.error != null) {
			return RexseeApplication.EXCEPTION_PREFIX + client.error;
		} else {
			String download = RexseeDownload.syncDownload(mBrowser, client.files, true);
			if (download.equals("")) {
				SharedPreferences preference = mContext.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
				Editor editor = preference.edit();
				editor.putInt(getVersionKey(client.xml), client.version);
				editor.commit();
			}
			return download;
		}
	}
}
